#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <sys/epoll.h>
#include <unistd.h>

#define NUM 64
#define SIZE 128

class Event;
class Reactor;

typedef int (*callback_t)(Event *); // 定义一个回调函数的指针

class Event
{
public:
    Event()
        : sock(-1), recver(nullptr), sender(nullptr), errorer(nullptr), R(nullptr)
    {
    }

    ~Event() = default;

    // 注册回调函数
    void RegisterCallback(callback_t _recv, callback_t _send, callback_t _error)
    {
        recver = _recv;
        sender = _send;
        errorer = _error;
    }

public:
    int sock;           // 对应的文件描述符
    callback_t recver;  // 接收回调方法
    callback_t sender;  // 发送回调方法
    callback_t errorer; // 异常回调方法

    std::string inbuffer;  // 接收缓冲区
    std::string outbuffer; // 发送缓冲区

    Reactor *R; // 指向外侧反应堆的指针
};

// 用Reactor管理Event
class Reactor
{
public:
    int epfd;
    std::unordered_map<int, Event *> events;

public:
    Reactor()
        : epfd(-1)
    {
    }

    void InitReactor()
    {
        int sock = epoll_create(SIZE);
        if (sock < 0)
        {
            std::cerr << "epfd create fail" << std::endl;
            exit(6);
        }
        epfd = sock;
        std::cout << "Reactor create success" << std::endl;
    }

    ~Reactor() = default;

    // 插入Event
    bool InsertEvent(Event *evp, uint32_t evs)
    {
        // 1. 在epoll中注册
        epoll_event ep;
        ep.data.fd = evp->sock;
        ep.events = evs;

        if (epoll_ctl(epfd, EPOLL_CTL_ADD, evp->sock, &ep) < 0)
        {
            std::cerr << "insert Event error" << std::endl;
            return false;
        }

        // 2. 插入反应堆中
        events[evp->sock] = evp;

        std::cout << "将" << evp->sock << "加入反应堆" << std::endl;
        return true;
    }

    // 删除Event
    bool DeleteEvent(Event *evp)
    {
        int fd = evp->sock;
        auto it = events.find(fd);
        if (it == events.end())
        {
            std::cerr << "delete Event error" << std::endl;
            return false;
        }

        // 1. 将fd从epfd的管理下删除
        if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr) < 0)
        {
            std::cerr << "delete fd error" << std::endl;
            return false;
        }
        // 2. 将fd从反应堆删除
        events.erase(it);
        // 3. 关闭fd
        close(fd);
        // 4. 删除Event
        delete evp;

        std::cout << "已将" << evp->sock << "从反应堆中删除" << std::endl;
        return true;
    }

    // 修改权限
    void ChangModeRW(Event *evp, bool IsRecv, bool IsWrite)
    {
        int sock = evp->sock;
        epoll_event ev;
        ev.data.fd = sock;

        ev.events = (IsRecv ? EPOLLIN : 0) | (IsWrite ? EPOLLOUT : 0);

        if (epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ev) < 0)
        {
            std::cerr << "change mode error" << std::endl;
            return;
        }
    }

    bool IsSockOk(int sock)
    {
        return events.find(sock) != events.end();
    }

    // 派发器
    void Dispatcher(int timeout)
    {
        epoll_event fd_array[NUM];

        int n = epoll_wait(epfd, fd_array, NUM, timeout);

        if (n > 0)
        {
            std::cout << "有事件就绪" << std::endl;

            for (int i = 0; i < n; ++i)
            {
                int sock = fd_array[i].data.fd;
                uint32_t revents = fd_array[i].events;

                // EAGAIN等错误以及对方关闭等错误，最后会通过EPOLLERR和EPOLLHUP返回
                // 虽然EPOLLIN也可以识别
                // 我们这里统一将异常放到 接收和发送 回调中处理
                if(revents & EPOLLERR)
                    revents = EPOLLIN | EPOLLOUT;
                if(revents & EPOLLHUP)
                    revents = EPOLLIN | EPOLLOUT;

                if(revents & EPOLLIN)
                {
                    if(IsSockOk(sock) && events[sock]->recver)
                        events[sock]->recver(events[sock]);
                }

                if(revents & EPOLLOUT)
                {
                    if(IsSockOk(sock) && events[sock]->sender)
                        events[sock]->sender(events[sock]);
                }
            }
        }
        else if (n == 0)
        {
            std::cout << "等待超时" << std::endl;
        }
        else
        {
            std::cerr << "等待出错" << std::endl;
            exit(7);
        }
    }
};